package com.ljc.db.backend.vm;

import com.ljc.db.backend.tm.TransactionManager;

public class Visibility {

    /**
     * 检查现在修改是否存在版本跳跃【存在返回true,不存在返回false】
     */
    public static boolean isVersionSkip(TransactionManager tm, Transaction t, Entry e) {
        // 获取更新数据版本的最新事务
        long xmax = e.getXmax();
        // 隔离级别为 读提交【level = 0】，允许版本条约，直接返回false
        if(t.level == 0) {
            return false;
        } else {
            // 如果隔离级别为  可重复读 则要判断
            // 【如果更新数据的事务进行了提交 并且 （更新数据的事务在当前事务之后[这是判断x1事务先于x2事务开启，但是x2修改了数据，x1不允许修改的情况]
            // 或者 **本事务开始时该事务还在活跃状态**[这是判断x1事务先于x2事务开启，但是x1修改了数据，x2不允许修改的情况]）】则存在版本跳跃
            return tm.isCommitted(xmax) && (xmax > t.xid || t.isInSnapshot(xmax));
        }
    }

    /**
     * 根据隔离级别判断版本是否可见
     */
    public static boolean isVisible(TransactionManager tm, Transaction t, Entry e) {
        if(t.level == 0) {
            return readCommitted(tm, t, e);
        } else {
            return repeatableRead(tm, t, e);
        }
    }

    /**
     * 完成读已提交功能【判断当前事务能否看到该版本的数据】
     */
    private static boolean readCommitted(TransactionManager tm, Transaction t, Entry e) {
        long xid = t.xid;
        long xmin = e.getXmin();
        long xmax = e.getXmax();
        // 如果当前数据的创建事务编号是当前事务【XMIN==xid】，并且没有其他事务对其进行修改【XMAX==0】，
        // 则说明当前版本的数据对当前事务是可见的
        if(xmin == xid && xmax == 0) return true;

        // ----->走到这里说明 当前事务不是创建该版本数据的事务 或者 有其他事务对数据进行了修改

        // 判断创建该版本数据的事务是否提交
        if(tm.isCommitted(xmin)) {
            // 如果创建该版本数据的事务进行了提交，并且没有其他事务对数据进行修改【XMAX==0】，则该版本数据对当前事务可见
            if(xmax == 0) return true;
            // 如果被其他事务修改了，并且修改当前数据版本的事务并不是当前事务
            if(xmax != xid) {
                // 则需要判断最后修改到该版本的事务是否提交，如果提交则当前事务能看到该版本数据
                if(!tm.isCommitted(xmax)) {
                    return true;
                }
            }
        }
        // 其他情况都看不到该版本数据
        return false;
    }

    /**
     *  完成可重复读功能【当前事务只能看到在当前事务开始之前提交的数据】
     */
    private static boolean repeatableRead(TransactionManager tm, Transaction t, Entry e) {
        long xid = t.xid;
        long xmin = e.getXmin();
        long xmax = e.getXmax();
        // 如果当前数据的创建事务编号是当前事务【XMIN==xid】，并且没有其他事务对其进行修改【XMAX==0】，
        // 则说明当前版本的数据对当前事务是可见的
        if(xmin == xid && xmax == 0) return true;

        // ----->走到这里说明 当前事务不是创建该版本数据的事务 或者 有其他事务对数据进行了修改

        // 如果创建该版本数据的事务已经提交，并且是在当前事务开始之前提交的【xmin<xid】，并且创建创建该版本数据的事务不是活跃状态
        if(tm.isCommitted(xmin) && xmin < xid && !t.isInSnapshot(xmin)) {
            // 进入判断内部，判断是否没有其他事务对数据进行修改【XMAX==0】，则该版本数据对当前事务可见
            if(xmax == 0) return true;
            // 如果该版本数据被其他事务修改过
            if(xmax != xid) {
                // 则要判断，修改这个数据的事物是否提交？没有提交过 或者 在当前事务开始之后提交 或者 修改数据的事物还是活跃状态
                if(!tm.isCommitted(xmax) || xmax > xid || t.isInSnapshot(xmax)) {
                    return true;
                }
            }
        }
        // 其他情况都看不到该版本数据
        return false;
    }

}
